package org.scau.studentevaluation.config.global.log;

import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Preconditions;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.aop.support.AopUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @Author: x1aolone
 * @Date: 2020/8/22 20:10
 */
@Aspect
@Component
@Slf4j
public class LogAspect {

    private final String LOG_BEGIN = "==================== begin ====================";
    private final String LOG_END = "====================  end  ====================\n";


    /**
     * 针对使用@WebLog注解的类或方法
     * @annotation针对使用在方法上的注解
     * @within针对使用在类上的注解
     */
    @Pointcut("@within(org.scau.studentevaluation.config.global.log.WebLog) || " +
            "@annotation(org.scau.studentevaluation.config.global.log.WebLog)")
    public void webLog(){}


    public String getArgsJson(String[] argNames, Object[] argValues) {
        JSONObject args = new JSONObject();
        int length = Math.max(argValues.length, argNames.length);
        for (int i = 0; i < length; i++) {
            if (argValues[i] instanceof ServletResponse) {
                args.put(argNames[i],"ServletResponse Value");
            } else if (argValues[i] instanceof ServletRequest) {
                args.put(argNames[i],"ServletRequest Value");
            } else {
                args.put(argNames[i], argValues[i]);
            }
        }
        return args.toJSONString();
    }


    @Before("webLog()")
    public void before (JoinPoint joinPoint) {
        log.info(LOG_BEGIN);
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        log.info("args: {}", getArgsJson(signature.getParameterNames(),joinPoint.getArgs()));
        log.info("signature: {}", signature);

        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        Preconditions.checkNotNull(attributes);
        HttpServletRequest request = attributes.getRequest();

        log.info("URI: {}", request.getRequestURI());

        Method method = signature.getMethod();
        List<Annotation> descriptions = new ArrayList<>();
        descriptions.add(method.getAnnotation(WebLog.class));
        descriptions.add(method.getDeclaringClass()
                .getAnnotation(WebLog.class)
        );
        descriptions.forEach(x -> {
            if (x != null) {
                log.info("description: {}", ((WebLog)x).value());
            }
        });

        log.info("request type: {}", request.getMethod());
        log.info("requester IP: {}", request.getRemoteAddr());
    }

    @Around("webLog()")
    public Object around (ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        try {
            Object result = proceedingJoinPoint.proceed();
            log.info("result: {}", result);
            return result;
        } catch (Throwable e) {
            // 让全局异常处理来处理这个异常
            log.error("webLog process error msg: {}", e.getMessage());
            throw e;
        }
    }

    @After("webLog()")
    public void after () {
        // 无论around执行具体方法会不会有异常，after都会在around之后执行
        log.info(LOG_END);
    }

}